#include "main_area.h"

MainArea::MainArea(QWidget* parent) : QFrame(parent)
{
    setAutoFillBackground(true);
    QPalette palette;
    QBrush brush(QColor(255, 255, 255, 255));
    brush.setStyle(Qt::SolidPattern);
    palette.setBrush(QPalette::Active, QPalette::Base, brush);
    palette.setBrush(QPalette::Active, QPalette::Window, brush);
    palette.setBrush(QPalette::Inactive, QPalette::Base, brush);
    palette.setBrush(QPalette::Inactive, QPalette::Window, brush);
    palette.setBrush(QPalette::Disabled, QPalette::Base, brush);
    palette.setBrush(QPalette::Disabled, QPalette::Window, brush);
    this->setPalette(palette);

    this->mouseDown = false;
    this->mouseGotDown.setX(0);
    this->mouseGotDown.setY(0);

    this->setLayout(new QHBoxLayout());

    drag_frame = new ResizeTool(this);
    connect(drag_frame, SIGNAL(needResize(int, int)), this, SLOT(resize(int, int)));

    grid_mode = false;

    srand(time(NULL));
}

MainArea::~MainArea()
{
    delete drag_frame;
}

void MainArea::resize(int deltaWidth, int deltaHeight)
{
    int newWidth = width()+deltaWidth>50 ? width()+deltaWidth : 50;
    int newHeight = height()+deltaHeight>50 ? height()+deltaHeight : 50;
    setGeometry(x(), y(), newWidth, newHeight);
}

Area * MainArea::addArea(int x, int y, int width, int height, int serial, AreaType type)
{
    // TODO: doesnt looks good, think about refactoring
    if (serial == -1)
        serial = rand();

    Area *area;
    switch (type)
    {
    case TextType:
        area = new TextArea(this, x, y, width, height, element.size(), serial);
        break;
    case ImageType:
        area = new ImageArea(this, x, y, width, height, element.size(), serial);
        break;
    case FileType:
        area = new FileArea(this, x, y, width, height, element.size(), serial);
        break;
    case PinType:
        area = new PinArea(this, x, y, width, height, element.size(), serial);
        break;
    }
    connect(area, SIGNAL(areaToBeMoved(int, int)), this, SLOT(moveSelectedAreas(int, int)));
    connect(area, SIGNAL(deselectOtherAreas(int)), this, SLOT(unselectAll(int)));
    element.push_back(area);
    return area;
}

// Adding a TextArea
void MainArea::addText(int X, int Y, int W, int H, int serial, const QString & data)
{
    this->store_to_buffer();
    TextArea * area = (TextArea *)addArea(X,Y, W, H, serial, TextType);
    area->setData(data);
    area->show();
    this->update();
}

// Adding an imagearea
void MainArea::addImage(int X, int Y, int W, int H, int serial, const QByteArray & data, bool default_size)
{
    this->store_to_buffer();
    QByteArray buffer;
    if (data=="")
    {
        QFile datafile(QFileDialog::getOpenFileName(this, "Select image file", 0, tr("Image Files (*.png *.jpg *.jpeg *.bmp *.gif *.tiff)")));
        if (!datafile.open(QIODevice::ReadOnly)) return;
        buffer = datafile.readAll();
        datafile.close();
    }
    else buffer = data;
    ImageArea * area = (ImageArea *)addArea(X,Y, W, H, serial, ImageType);
    area->setData(buffer, default_size);
    area->show();
    this->update();
}

void MainArea::addFile(int X, int Y, int W, int H, int serial, const QString & data)
{
    this->store_to_buffer();

    QString filename = "";
    if (data=="")
        filename = QFileDialog::getOpenFileName(this, "Select file", 0);
    else
        filename = data;

    FileArea * area = (FileArea *)addArea(X,Y, W, H, serial, FileType);
    area->setData(filename);
    area->show();
    this->update();
}

void MainArea::addPin(int X, int Y, int W, int H, int serial, const QString & data)
{
    this->store_to_buffer();

    QString filename = "";
    if (data=="")
    {
        filename = QFileDialog::getOpenFileName(NULL, "Open...", 0, tr("Pinoteo Save Files (*.pin)"));
    }
    else
        filename = data;

    PinArea * area = (PinArea *)addArea(X,Y, W, H, serial, PinType);
    area->setData(filename);
    area->show();
    this->update();
}

// Adding a line
void MainArea::addLine()
{
    this->store_to_buffer();
    int counter = 0;
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (area->selected()) counter++;
    }
    if (counter < 2)
    {
        QMessageBox::information(this, "Drawing a line", "You should have at least 2 objects selected, to draw a line between them!");
        return;
    }

    for(int i=0; i<element.size(); i++)
    {
        Area * src = element[i];
        if (src==NULL) continue;
        if(!src->selected()) continue;
        for(int k=i+1; k<element.size(); k++)
        {
            Area * dest = element[k];
            if (dest==NULL) continue;
            if(!dest->selected()) continue;
            this->line.push_back(new VectorLine(src, dest));
        }

    }

    this->update();
}

void MainArea::delLine()
{
    this->store_to_buffer();
    int counter = 0;
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (area->selected()) counter++;
    }
    if (counter < 2)
    {
        QMessageBox::information(this, "Drawing a line", "You should have at least 2 objects selected, to delete a line between them!");
        return;
    }

    for(int i=0; i<element.size(); i++)
    {
        Area * src = element[i];
        if (src==NULL) continue;
        if(!src->selected()) continue;
        for(int k=i+1; k<element.size(); k++)
        {
            Area * dest = element[k];
            if (dest==NULL) continue;
            if(!dest->selected()) continue;

            for(int j=0; j<line.size(); j++)
            {
                VectorLine *l = line[j];
                if (l==NULL) continue;
                if (l->isEqual(src, dest))
                {
                    delete l;
                    line[j] = 0;
                    line.erase(this->line.begin()+j,this->line.begin()+j);
                }
            }
        }

    }
    this->update();
}

void MainArea::addLine(Area *src, Area *dest)
{
    if ((src==0) || (dest==0)) return;
    this->line.push_back(new VectorLine(src, dest));
}

// Deleting Area
void MainArea::deleteArea(int id)
{
    //this->store_to_buffer();
    Area * area = element[id];
    delete area;
    element[id] = 0;
    this->check_for_bad_lines();
    this->element.erase(this->element.begin()+id,this->element.begin()+id);
    this->update();
}

void MainArea::deleteAreas()
{
    //this->store_to_buffer();
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        this->deleteArea(area->id());
    }
}

void MainArea::deleteSelectedAreas()
{
    this->store_to_buffer();
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (area->selected())
        {
            this->deleteArea(area->id());
        }
    }
}

void MainArea::mousePressEvent( QMouseEvent * e )
{
    if ( e->button() == Qt::LeftButton )
    {
        this->mouseDown = true;
        this->mouseGotDown.setX(e->x());
        this->mouseGotDown.setY(e->y());
        this->mouseNow.setX(e->x());
        this->mouseNow.setY(e->y());
        unselectAll(-1);

        //qDebug(QString::number(this->nextAction).toStdString().c_str());

        switch ( this->nextAction )
        {
            case TEXT:
                addText(e->x(), e->y());
                break;
            case IMG:
                addImage(e->x(), e->y());
                break;
            case LINK:
                addFile(e->x(), e->y());
                break;
            case PIN:
                addPin(e->x(), e->y());
                break;
            case CURSOR:

                break;
        }
    }
}

void MainArea::unselectAll(int eid)
{
    for(int i=0; i<element.size(); i++)
    {
        if(i==eid) continue;
        Area * area = element[i];
        if(area != NULL)
        {
            area->deSelect();
            if(area->isEmpty()) deleteArea(area->id());
        }
    }
    this->setFocus();
}

void MainArea::selectAll()
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if(area != NULL)
        {
            area->select();
            if(area->isEmpty()) deleteArea(area->id());
        }
    }
}

QByteArray MainArea::Store()
{
    QByteArray store_info;
    //store_info += "<main>"+QByteArray::number(this->width())+"x"+QByteArray::number(this->height())+"</main>\n";
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if(area != NULL)
        {
            store_info += area->contains();
        }
    }

    for(int i=0; i<line.size(); i++)
    {
        VectorLine * this_line = line[i];
        if (this_line == NULL) continue;
        store_info += this_line->contains();
    }

    return store_info;
}

QByteArray MainArea::store_selected()
{
    QByteArray store_info;
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if(area != NULL)
        {
            if (!area->selected()) continue;
            store_info += area->contains();
        }
    }

    /*for(int i=0; i<line.size(); i++)
    {
        VectorLine * this_line = line[i];
        if (this_line == NULL) continue;
        store_info += this_line->contains();
    }*/

    return store_info;
}

void MainArea::paintEvent(QPaintEvent *)
{
    for(int i=0; i<line.size(); i++)
    {
        VectorLine *l = line[i];
        if (l==NULL) continue;
        l->draw(this);
    }
    if ((this->nextAction == CURSOR) && (this->mouseDown))
    {
        QPainter *painter = new QPainter();
        painter->begin(this);
            painter->setPen(QColor(100, 100, 100));
            painter->drawRect(QRect(mouseGotDown, mouseNow));
        painter->end();
        this->update();
    }

    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if(area != NULL)
        {
            //area->updateArea();
            // Проверям размеры полей
            if (area->x()+area->width() > this->width()) this->setGeometry(0,0,area->x()+area->width()+5, this->height());
            if (area->y()+area->height() > this->height()) this->setGeometry(0,0,this->width(), area->y()+area->height()+5);
        }
    }

    /////////////////////////////


}

void MainArea::mouseReleaseEvent(QMouseEvent *e)
{
    this->mouseDown = false;
    this->mouseGotDown.setX(e->x());
    this->mouseGotDown.setY(e->y());
}

void MainArea::mouseMoveEvent(QMouseEvent *e)
{
    this->mouseNow.setX(e->x());
    this->mouseNow.setY(e->y());

    int x1, x2, y1, y2;

    if (mouseNow.x() < mouseGotDown.x())
    {
        x1 = mouseNow.x();
        x2 = mouseGotDown.x();
    }
    else
    {
        x2 = mouseNow.x();
        x1 = mouseGotDown.x();
    }

    if (mouseNow.y() < mouseGotDown.y())
    {
        y1 = mouseNow.y();
        y2 = mouseGotDown.y();
    }
    else
    {
        y2 = mouseNow.y();
        y1 = mouseGotDown.y();
    }

    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if ( (area->x() > x1-area->width()) && (area->x() < x2) && (area->y() > y1-area->height()) && (area->y() < y2))
            area->select();
        else area->deSelect();
    }
}

void MainArea::printPreview()
{
   QPrinter printer;
   QPrintPreviewDialog preview( &printer, this );
   connect( &preview, SIGNAL(paintRequested(QPrinter *) ),
                      this, SLOT(printDocument(QPrinter*)));
   preview.exec();
}

void MainArea::printDocument( QPrinter *printer_ )
{
    printer_->setOrientation(QPrinter::Landscape);
    printer_->setFullPage( TRUE );
    //printer_->margins(0,0,1,1);
    this->render( printer_ );
}

Area *MainArea::get_area_by_serial(int serial)
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (area->serial() == serial) return area;
    }
    return NULL;
}

void MainArea::check_for_bad_lines()
{
    for(int i=0; i<line.size(); i++)
    {
        VectorLine *l = line[i];
        if (l==NULL) continue;
        if ( get_area_by_serial(l->dest()->serial())==NULL || this->get_area_by_serial(l->src()->serial())==NULL )
        {
            delete l;
            line[i] = 0;
            this->line.erase(this->line.begin()+i,this->line.begin()+i);
        }
    }
}

bool MainArea::ask4confirm()
{
    if (element.size() > 0)
        if(QMessageBox::question(this, "Message", "All unsaved data will be lost! Are you sure?",
                                     QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
            return false;
        return true;
}

void MainArea::moveSelectedAreas(int x, int y)
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if(area->selected()==false) continue;
        area->setGeometry(area->x()+x, area->y()+y, area->width(), area->height());
        //area->update();
    }
    this->update();
}

void MainArea::areas_to_grid()
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        area->toGrid();
    }
}

void MainArea::store_to_buffer()
{
    this->previous_state = this->Store();
}

QByteArray MainArea::restore_from_buffer()
{
    this->deleteAreas();
    return this->previous_state;
}

void MainArea::resizeEvent(QResizeEvent *)
{
    drag_frame->setGeometry(this->width()-12,this->height()-12,12,12);
}

void MainArea::restore_from_ex_buffer(QByteArray buffer)
{
    SmartByteArray data(buffer);

    QList<QByteArray> s_data = data.split("</object>\n", 10);

    for(int i=0; i<s_data.count(); i++)
    {
        SmartByteArray temp_(s_data[i]);
        QList<QByteArray> temp_s_ = temp_.split(">",1,1);
        QString params_str = temp_s_[0];
        QByteArray data = temp_s_[1];
        QStringList params = params_str.split(" ");
        QString type = params[1].split("=")[1];

        if (type=="line")
        {
            int src_s = params[2].split("=")[1].toInt();
            int dest_s = params[3].split("=")[1].toInt();

            Area *src, *dest;
            src = this->get_area_by_serial(src_s);
            dest = this->get_area_by_serial(dest_s);

            this->addLine(src, dest);
            continue;
        }

        int x = params[2].split("=")[1].toInt();
        int y = params[3].split("=")[1].toInt();
        int w = params[4].split("=")[1].toInt();
        int h = params[5].split("=")[1].toInt();
        int serial = params[6].split("=")[1].toInt();

        if(type == "image")
            this->addImage(x, y, w, h, serial, data, false);
        else if (type == "text")
        {
            QString data_ = QString::fromUtf8(data);
            this->addText(x, y, w, h, serial, data_);
        }

    }

}

void MainArea::set_font_for_areas()
{
    int counter = 0;
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if ((area==NULL) || (!area->selected())) continue;
        if (typeid(*area) != typeid(TextArea)) continue;
        counter++;
    }
    if (counter == 0)
    {
        QMessageBox::information(this, "Setting up font", "You should have at least 1 text object selected to set up its font!");
        return;
    }
    bool ok;
    QFont font = QFontDialog::getFont( &ok, this );
    if ( ok ) {
        for(int i=0; i<element.size(); i++)
        {
            Area * area = element[i];
            if ((area==NULL) || (!area->selected())) continue;
            if (typeid(*area) != typeid(TextArea)) continue;
            dynamic_cast<TextArea *>( area )->setFont(font);

        }
    }
}

void MainArea::set_color_for_areas()
{
    int counter = 0;
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if ((area==NULL) || (!area->selected())) continue;
        counter++;
    }
    if (counter == 0)
    {
        QMessageBox::information(this, "Setting up color", "You should have at least 1 text object selected to set up its color!");
        return;
    }
    bool ok;
    QColor color = QColorDialog::getColor(QColor(0, 85, 127, 255));
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if ((area==NULL) || (!area->selected())) continue;
        area->setColor(color);

    }
}

void MainArea::zoom_out()
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        area->setGeometry(area->x()/1.2, area->y()/1.2, area->width()/1.2, area->height()/1.2);
        if (typeid(*area) == typeid(TextArea))
        {
            dynamic_cast<TextArea *>( area )->fontZoomOut();
        }
    }
    this->update();
}

void MainArea::zoom_in()
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (typeid(*area) == typeid(TextArea))
        {
            dynamic_cast<TextArea *>( area )->fontZoomIn();
        }
        area->setGeometry(area->x()*1.2, area->y()*1.2, area->width()*1.2, area->height()*1.2);
    }
    this->update();
}

void MainArea::load_next_document()
{
    for(int i=0; i<element.size(); i++)
    {
        Area * area = element[i];
        if (area==NULL) continue;
        if (typeid(*area) == typeid(PinArea))
        {
            QByteArray data = dynamic_cast<PinArea *>( area )->getData();
            this->deleteAreas();
            this->parse_data( data );
            this->update();
            return;
        }
    }
    QMessageBox::information(this, "Next document", "No next document object selected!");
    return;

}

bool MainArea::parse_data(SmartByteArray data)
{
    QList<QByteArray> s_data = data.split("</object>\n", 10);

    for(int i=0; i<s_data.count(); i++)
    {
        SmartByteArray temp_(s_data[i]);
        QList<QByteArray> temp_s_ = temp_.split(">",1,1);
        QString params_str = temp_s_[0];
        QByteArray data = temp_s_[1];
        QStringList params = params_str.split(" ");

        QString type = params[1].split("=")[1];

        //line is an unusual object - it doesnt have any geometry
        if (type=="line")
        {
            int src_s = params[2].split("=")[1].toInt();
            int dest_s = params[3].split("=")[1].toInt();
            Area *src, *dest;
            src = this->get_area_by_serial(src_s);
            dest = this->get_area_by_serial(dest_s);
            this->addLine(src, dest);
            continue;
        }

        //geometry
        int x = params[2].split("=")[1].toInt();
        int y = params[3].split("=")[1].toInt();
        int w = params[4].split("=")[1].toInt();
        int h = params[5].split("=")[1].toInt();
        // colors
        int r = params[6].split("=")[1].toInt();
        int g = params[7].split("=")[1].toInt();
        int b = params[8].split("=")[1].toInt();
        //serial
        int serial = params[9].split("=")[1].toInt();
        //check types

        if(type == "image")
        {
            this->addImage(x, y, w, h, serial, data, false);
            this->get_area_by_serial(serial)->setColor(QColor(r,g,b,255));
        }
        else if (type == "text")
        {
            QString data_ = QString::fromUtf8(data);
            this->addText(x, y, w, h, serial, data_);
            this->get_area_by_serial(serial)->setColor(QColor(r,g,b,255));
        }
        else if (type == "file")
        {
            this->addFile(x, y, w, h, serial, QString::fromUtf8(data.left(data.length()-1)));
            this->get_area_by_serial(serial)->setColor(QColor(r,g,b,255));
        }
        else if (type == "pin")
        {
            this->addPin(x, y, w, h, serial, QString::fromUtf8(data.left(data.length()-1)));
            this->get_area_by_serial(serial)->setColor(QColor(r,g,b,255));
        }

    }


    //this->setTitle(filename);

    return true;
}
